#include "WhyStateSystem.h"

#include "../Helper/Util.h"

void WhyStateSystem::SendCmd(GameStateCmdType cmdType, const char* stateName, float delay)
{
	char* name = NULL;
	if(stateName != NULL)
		name = CopyString(stateName);

	GameStateCmd cmd;
	cmd.cmd = cmdType;
	cmd.stateName = name;
	cmd.delay = currenttime + delay;

	cmdQueue.push(cmd);
}

void WhyStateSystem::RegisterState(IGameState* state)
{
	registeredStates[state->GetName()] = state;

	if(registeredStates.size() == 1)
	{
		currenttime = 0;
		stateStack.push_back(state);
		SendCmd(CMD_FIRST, state->GetName());
	}
}

void WhyStateSystem::ChangeState(const char* stateName, float delay, bool shouldFlush)
{
	if(shouldFlush)
	{
		while(!cmdQueue.empty())
		{
			cmdQueue.pop();
		}
	}

	SendCmd(CMD_CHANGE, stateName, delay);
}

void WhyStateSystem::PushState(const char* stateName, float delay, bool shouldFlush)
{
	if(shouldFlush)
	{
		while(!cmdQueue.empty())
		{
			cmdQueue.pop();
		}
	}

	SendCmd(CMD_PUSH, stateName, delay);
}

void WhyStateSystem::PopState(int numStatesToPop, float delay, bool shouldFlush)
{
	if(shouldFlush)
	{
		while(!cmdQueue.empty())
		{
			cmdQueue.pop();
		}
	}

	SendCmd(CMD_POP, NULL, delay);

	while(numStatesToPop > 1)
	{
		SendCmd(CMD_POP, NULL, 0.0f);
		numStatesToPop--;
	}
}

void WhyStateSystem::PopAllStates(float delay, bool shouldFlush)
{
	if(shouldFlush)
	{
		while(!cmdQueue.empty())
		{
			cmdQueue.pop();
		}
	}

	SendCmd(CMD_POPALL, NULL, delay);
}

void WhyStateSystem::ProcessCmdQueue()
{
	if(cmdQueue.empty())
	{
		currenttime = 0.0f;
		return;
	}

	while((!cmdQueue.empty()) && (cmdQueue.front().delay <= currenttime))
	{
		GameStateCmd cmd = cmdQueue.front();
		cmdQueue.pop();

		const char* prevname = GetCurrentStateName();

		switch (cmd.cmd)
		{
		case CMD_PUSH:
			{
				if (!_stricmp(prevname, cmd.stateName))
				{
					GetCurrentState()->OnOverride(cmd.stateName);

					stateStack.push_back(GetState(cmd.stateName));

					GetCurrentState()->OnEnter(prevname);
				}
			}
			break;
		case CMD_POP:
			{
				if (stateStack.size() > 1)
				{
					GetCurrentState()->OnExit(stateStack[stateStack.size() - 2]->GetName());

					stateStack.pop_back();

					GetCurrentState()->OnResume(prevname);
				}
			}
			break;

		case CMD_POPALL:
			{
				while (stateStack.size() > 1)
				{
					GetCurrentState()->OnExit(stateStack[stateStack.size() - 2]->GetName());

					stateStack.pop_back();

					GetCurrentState()->OnResume(prevname);
				}
			}
			break;

		case CMD_CHANGE:
			{
				if (!_stricmp(prevname, cmd.stateName))
				{
					GetCurrentState()->OnExit(cmd.stateName);

					stateStack.pop_back();
					stateStack.push_back(GetState(cmd.stateName));

					GetCurrentState()->OnEnter(prevname);
				}
			}
			break;

		case CMD_FIRST:
			{
				GetCurrentState()->OnEnter(NULL);
			}
			break;
		}

		if(cmd.stateName != NULL)
			delete[] cmd.stateName;
	}
}

void WhyStateSystem::ShutDown()
{
	for(unsigned i = 0; i < stateStack.size(); ++i)
	{
		stateStack[i]->OnExit(NULL);
	}

	stateStack.clear();

	map<const char*, IGameState*>::iterator iter;

	for(iter = registeredStates.begin(); iter != registeredStates.end(); ++iter)
	{
		delete (*iter).second;
	}

	registeredStates.clear();
}

void WhyStateSystem::Update(float deltatime)
{
	currenttime += deltatime;

	ProcessCmdQueue();

	GetCurrentState()->Input();

	for(int i = stateStack.size() - 1; i >= 0; --i)
		stateStack[i]->Update();
}

void WhyStateSystem::Render()
{
	for(unsigned i = 0; i < stateStack.size(); ++i)
		stateStack[i]->Render();
}